﻿using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using TCPBaseModbusTCP.Models;

namespace TCPBaseModbusTCP.Services;

public class TcpServerService : BackgroundService
{
    private readonly TcpListener _tcpListener;
    private readonly TcpServerSetting _settings;
    private readonly Dictionary<string, ClientConnection> _connections = new();
    private readonly object _lockObj = new();
    private readonly Regex _clientMessageRegex;

    // 客户端连接信息
    private class ClientConnection
    {
        public TcpClient Client { get; set; }
        public string ClientId { get; set; }
        public string ClientType { get; set; } // "Gateway"或"Client"
        public NetworkStream Stream { get; set; }
        public IPEndPoint RemoteEndPoint { get; set; }
    }

    public TcpServerService(IOptions<TcpServerSetting> settings)
    {
        _settings = settings.Value;
        _tcpListener = new TcpListener(IPAddress.Parse(_settings.IpAddress), _settings.Port);
        _clientMessageRegex = new Regex(_settings.ClientMessagePattern, RegexOptions.Compiled);
    }

    // 获取当前所有连接
    public List<(string ClientId, string ClientType, string RemoteEndPoint)> GetConnections()
    {
        lock (_lockObj)
        {
            return _connections.Values.Select(c =>
                (c.ClientId, c.ClientType, c.RemoteEndPoint.ToString())
            ).ToList();
        }
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _tcpListener.Start();
        Console.WriteLine($"TCP服务端已启动（Modbus TCP兼容），监听 {_settings.IpAddress}:{_settings.Port}");
        Console.WriteLine($"网关IP白名单: {string.Join(", ", _settings.GatewayIpWhitelist)}");

        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                var client = await _tcpListener.AcceptTcpClientAsync(stoppingToken);
                _ = HandleClientConnectionAsync(client, stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            // 正常关闭
        }
        finally
        {
            _tcpListener.Stop();
            Console.WriteLine("TCP服务端已停止");
        }
    }

    // 处理客户端连接
    private async Task HandleClientConnectionAsync(TcpClient client, CancellationToken stoppingToken)
    {
        ClientConnection connection = null;
        try
        {
            var remoteEndPoint = (IPEndPoint)client.Client.RemoteEndPoint!;
            var stream = client.GetStream();
            string clientIp = remoteEndPoint.Address.ToString();
            string clientType = "Unknown";
            string clientId = $"Unknown-{clientIp}:{remoteEndPoint.Port}";

            Console.WriteLine($"新连接: {remoteEndPoint}");

            // 1. 优先尝试识别普通客户端（无论IP是否在白名单）
            // 优先尝试识别普通客户端（通过元组接收结果）
            var (isNormalClient, normalClientId) = await TryIdentifyNormalClientAsync(
                client, stream, remoteEndPoint, stoppingToken);
            if (isNormalClient && !string.IsNullOrEmpty(normalClientId))
            {
                clientType = "Client";
                clientId = $"C-{normalClientId}";
                Console.WriteLine($"识别为普通客户端: {clientId} @ {remoteEndPoint}");

                connection = new ClientConnection
                {
                    Client = client,
                    ClientId = clientId,
                    ClientType = clientType,
                    Stream = stream,
                    RemoteEndPoint = remoteEndPoint
                };
                AddConnection(connection);

                // 处理普通客户端数据
                await ProcessClientDataAsync(connection, stoppingToken);
                return; // 普通客户端处理逻辑已启动，直接返回
            }

            // 2. 普通客户端验证失败，再检查是否为网关（IP在白名单中）
            if (IsIpInWhitelist(clientIp))
            {
                clientType = "Gateway";
                clientId = $"GW-{clientIp}";
                Console.WriteLine($"IP在白名单中，识别为Modbus网关: {clientId}");

                connection = new ClientConnection
                {
                    Client = client,
                    ClientId = clientId,
                    ClientType = clientType,
                    Stream = stream,
                    RemoteEndPoint = remoteEndPoint
                };
                AddConnection(connection);

                // 处理Modbus网关数据
                await ProcessModbusGatewayDataAsync(connection, stoppingToken);
            }
            else
            {
                // 既不是普通客户端也不是网关，断开连接
                Console.WriteLine($"连接{remoteEndPoint}既不是有效客户端也不在网关白名单，断开连接");
                await SendResponseAsync(stream, "ERROR: 未通过身份验证且不在网关白名单");
                client.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接处理异常: {ex.Message}");
        }
        finally
        {
            if (connection != null)
            {
                RemoveConnection(connection.ClientId);
                connection.Stream?.Dispose();
                connection.Client?.Close();
                Console.WriteLine($"连接断开: {connection.ClientId}");
            }
        }
    }

    // 尝试识别普通客户端（提取为独立方法）
    private async Task<(bool IsValid, string ClientId)> TryIdentifyNormalClientAsync(
    TcpClient client,
    NetworkStream stream,
    IPEndPoint remoteEndPoint,
    CancellationToken stoppingToken)
    {
        try
        {
            using (var cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken))
            {
                cts.CancelAfter(_settings.HandshakeTimeoutMs); // 握手超时时间
                byte[] buffer = new byte[_settings.BufferSize];
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token);

                if (bytesRead == 0)
                {
                    Console.WriteLine($"客户端{remoteEndPoint}未发送数据，普通客户端验证失败");
                    return (false, null); // 返回元组：(验证失败, 空ID)
                }

                string handshake = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                var (valid, id) = ValidateClientHandshake(handshake);

                if (valid && !string.IsNullOrEmpty(id))
                {
                    await SendResponseAsync(stream, "ACK: 身份验证通过（普通客户端）");
                    return (true, id); // 返回元组：(验证成功, 客户端ID)
                }
                else
                {
                    Console.WriteLine($"客户端{remoteEndPoint}发送无效握手信息: {handshake}，普通客户端验证失败");
                    return (false, null);
                }
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine($"客户端{remoteEndPoint}握手超时，普通客户端验证失败");
            return (false, null);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"普通客户端验证异常: {ex.Message}");
            return (false, null);
        }
    }

    // 检查IP是否在网关白名单中
    private bool IsIpInWhitelist(string ipAddress)
    {
        return _settings.GatewayIpWhitelist.Any(whitelistIp =>
            ipAddress == whitelistIp ||
            (whitelistIp.EndsWith("*") && ipAddress.StartsWith(whitelistIp.Substring(0, whitelistIp.Length - 1)))
        );
    }

    // 验证普通客户端身份标识
    private (bool IsValid, string ClientId) ValidateClientHandshake(string handshake)
    {
        try
        {
            if (handshake.StartsWith("CLIENT_ID:"))
            {
                string clientId = handshake.Split(':', 2)[1].Trim();
                return (!string.IsNullOrEmpty(clientId), clientId);
            }
            return (false, null);
        }
        catch
        {
            return (false, null);
        }
    }

    // 处理Modbus网关数据（核心：协议验证 + 转发）
    private async Task ProcessModbusGatewayDataAsync(ClientConnection connection, CancellationToken stoppingToken)
    {
        byte[] buffer = new byte[_settings.BufferSize];

        while (!stoppingToken.IsCancellationRequested && connection.Client.Connected)
        {
            // 1. 读取数据（返回实际有效字节数）
            int bytesRead = await connection.Stream.ReadAsync(buffer, 0, buffer.Length, stoppingToken);
            if (bytesRead == 0)
            {
                Console.WriteLine("客户端断开连接");
                break;
            }

            // 2. 提取有效数据（仅前bytesRead字节，避免无效空字节）
            byte[] validData = new byte[bytesRead];
            Array.Copy(buffer, validData, bytesRead); // 复制有效部分

            // 3. 使用有效数据解析（传入validData和实际长度bytesRead）
            var (isValid, packet, errorMsg) = ParseAndValidateModbusPacket(validData, bytesRead);
            if (!isValid)
            {
                Console.WriteLine($"网关[{connection.ClientId}]消息格式错误: {errorMsg}");
                // 发送Modbus异常响应（功能码+0x80表示异常）
                var errorResponse = CreateModbusErrorResponse(packet, 0x01); // 0x01=非法功能
                await connection.Stream.WriteAsync(errorResponse, 0, errorResponse.Length, stoppingToken);
                continue;
            }

            // 2. 验证通过，打印Modbus消息详情
            Console.WriteLine($"收到网关[{connection.ClientId}]Modbus数据 - " +
                $"事务ID: {packet.TransactionId}, 功能码: 0x{packet.FunctionCode:X2}, " +
                $"数据长度: {packet.Data.Length}字节，数据：{packet.Data.ToString()}");

            // 3. 转发原始Modbus报文给所有普通客户端（保留协议完整性）
            bool forwardSuccess = await ForwardToAllClientsAsync(packet.RawBytes, stoppingToken);
            Console.WriteLine(forwardSuccess ? "已转发给所有普通客户端" : "无在线普通客户端，跳过转发");

            // 4. 向网关发送确认响应（可选，根据需求）
            var ackResponse = CreateModbusAckResponse(packet);
            await connection.Stream.WriteAsync(ackResponse, 0, ackResponse.Length, stoppingToken);
        }
    }

    // 解析并验证Modbus TCP报文（核心方法）
    private (bool IsValid, ModbusTcpPacket Packet, string ErrorMessage) ParseAndValidateModbusPacket(byte[] data, int length)
    {
        try
        {
            // 1. 基本长度验证（最少8字节：6字节MBAP头 + 1字节功能码 + 1字节数据）
            if (length < 8)
                return (false, null, $"报文过短（最少8字节，实际{length}字节）");

            // 2. 解析MBAP头（关键：处理大端序）
            // 临时数组用于存储反转后的字节（适配小端序系统）
            byte[] tempBytes = new byte[2];

            // 解析事务ID（2字节，大端序）
            Array.Copy(data, 0, tempBytes, 0, 2);
            if (BitConverter.IsLittleEndian) Array.Reverse(tempBytes); // 小端系统反转字节
            ushort transactionId = BitConverter.ToUInt16(tempBytes, 0);

            // 解析协议ID（2字节，大端序）
            Array.Copy(data, 2, tempBytes, 0, 2);
            if (BitConverter.IsLittleEndian) Array.Reverse(tempBytes);
            ushort protocolId = BitConverter.ToUInt16(tempBytes, 0);

            // 解析长度字段（2字节，大端序）—— 重点修复这里
            Array.Copy(data, 4, tempBytes, 0, 2);
            if (BitConverter.IsLittleEndian) Array.Reverse(tempBytes); // 反转后才能得到正确值
            ushort lengthField = BitConverter.ToUInt16(tempBytes, 0);

            // 构建数据包对象
            var packet = new ModbusTcpPacket
            {
                TransactionId = transactionId,
                ProtocolId = protocolId,
                Length = lengthField, // 现在这里会得到正确的长度（如7），而非1792
                UnitId = data[6],
                FunctionCode = data[7],
                RawBytes = new byte[length]
            };
            Array.Copy(data, packet.RawBytes, length);

            // 3. 验证MBAP头（现在长度校验会正确）
            if (packet.ProtocolId != 0)
                return (false, packet, $"无效协议ID（应为0，实际0x{packet.ProtocolId:X4}）");

            int actualRemainingLength = length - 6; // 实际后续长度（总长度 - 6字节MBAP头）
            if (packet.Length != actualRemainingLength)
                return (false, packet, $"长度字段不匹配（应为{actualRemainingLength}，实际{packet.Length}）");

            // 4. 验证功能码
            if (packet.FunctionCode < _settings.MinFunctionCode || packet.FunctionCode > _settings.MaxFunctionCode)
                return (false, packet, $"无效功能码（应为{_settings.MinFunctionCode}-{_settings.MaxFunctionCode}）");

            // 5. 解析数据区
            packet.Data = new byte[length - 8]; // 总长度 - 6字节MBAP头 - 1字节功能码 = 数据区长度
            Array.Copy(data, 8, packet.Data, 0, packet.Data.Length);

            return (true, packet, "验证通过");
        }
        catch (Exception ex)
        {
            return (false, null, $"解析失败: {ex.Message}");
        }
    }


    // 创建Modbus确认响应（简化版：复用事务ID，返回成功响应）
    private byte[] CreateModbusAckResponse(ModbusTcpPacket request)
    {
        // 响应格式：MBAP头（6字节） + 功能码（1字节） + 确认数据（1字节0x00表示成功）
        byte[] response = new byte[8];
        // 复制事务ID和协议ID
        Array.Copy(BitConverter.GetBytes(request.TransactionId), 0, response, 0, 2);
        Array.Copy(BitConverter.GetBytes((ushort)0), 0, response, 2, 2); // 协议ID固定0
        response[4] = 0x00; response[5] = 0x02; // 长度=2（UnitId + 功能码 + 数据）
        response[6] = request.UnitId; // 单元ID
        response[7] = request.FunctionCode; // 功能码（原样返回表示成功）
        return response;
    }

    // 创建Modbus异常响应（功能码+0x80，添加异常码）
    private byte[] CreateModbusErrorResponse(ModbusTcpPacket request, byte errorCode)
    {
        if (request == null)
        {
            // 对无效请求的默认异常响应
            return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, errorCode };
        }

        // 异常响应格式：MBAP头 + 功能码（原功能码+0x80） + 异常码
        byte[] response = new byte[9];
        Array.Copy(BitConverter.GetBytes(request.TransactionId), 0, response, 0, 2);
        Array.Copy(BitConverter.GetBytes((ushort)0), 0, response, 2, 2);
        response[4] = 0x00; response[5] = 0x03; // 长度=3
        response[6] = request.UnitId;
        response[7] = (byte)(request.FunctionCode | 0x80); // 功能码最高位设为1表示异常
        response[8] = errorCode; // 异常码（0x01=非法功能，0x02=非法地址等）
        return response;
    }

    // 处理普通客户端数据（格式验证）
    // 调整ProcessClientDataAsync方法（移除初始数据参数，统一在循环中处理）
    private async Task ProcessClientDataAsync(ClientConnection connection, CancellationToken stoppingToken)
    {
        byte[] buffer = new byte[_settings.BufferSize];

        while (!stoppingToken.IsCancellationRequested && connection.Client.Connected)
        {
            int bytesRead = await connection.Stream.ReadAsync(buffer, 0, buffer.Length, stoppingToken);
            if (bytesRead == 0) break;

            string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
            Console.WriteLine($"收到客户端[{connection.ClientId}]数据: {data}");

            // 验证普通客户端消息格式
            if (!_clientMessageRegex.IsMatch(data))
            {
                await SendResponseAsync(connection.Stream, "ERROR: 客户端消息格式无效（应为CMD:指令;PARAM:参数）");
                continue;
            }

            // 回复客户端
            await SendResponseAsync(connection.Stream, $"CLIENT_ACK: 已接收 [{data}]");
        }
    }

    // 转发消息给所有普通客户端
    private async Task<bool> ForwardToAllClientsAsync(byte[] data, CancellationToken stoppingToken)
    {
        List<ClientConnection> clients;
        lock (_lockObj)
        {
            clients = _connections.Values
                .Where(c => c.ClientType == "Client" && c.Client.Connected)
                .ToList();
        }

        if (clients.Count == 0)
            return false;

        // 逐个转发（忽略单个客户端的发送失败，不影响其他客户端）
        foreach (var client in clients)
        {
            try
            {
                if (client.Client.Connected)
                {
                    await client.Stream.WriteAsync(data, 0, data.Length, stoppingToken);
                    Console.WriteLine($"已转发数据到客户端: {client.ClientId}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"转发给客户端{client.ClientId}失败: {ex.Message}");
            }
        }
        return true;
    }

    // 发送文本响应（用于普通客户端）
    private async Task SendResponseAsync(NetworkStream stream, string message)
    {
        byte[] response = Encoding.UTF8.GetBytes(message);
        await stream.WriteAsync(response, 0, response.Length);
    }

    // 添加连接（线程安全）
    private void AddConnection(ClientConnection connection)
    {
        lock (_lockObj)
        {
            if (_connections.Count < _settings.MaxConnections)
            {
                _connections[connection.ClientId] = connection;
            }
        }
    }

    // 移除连接（线程安全）
    private void RemoveConnection(string clientId)
    {
        lock (_lockObj)
        {
            _connections.Remove(clientId);
        }
    }
}
